use std::sync::Arc;

use axum::{
    extract::{Path, Query, State},
    Json,
};
use chrono::Local;
use validator::Validate;

use crate::{
    db, filter, form,
    handler::{get_conn, log_error, AffResponse, IDResponse, Response},
    jwt::AdminClaimsData,
    middleware::RequiredAuth,
    model, service, AppState, Error, Result,
};

pub async fn add(
    State(state): State<Arc<AppState>>,
    RequiredAuth(cliams): RequiredAuth<AdminClaimsData>,
    Json(frm): Json<form::url::Add>,
) -> Result<Json<Response<IDResponse>>> {
    let handler_name = "admin/url/add";
    frm.validate()
        .map_err(Error::from)
        .map_err(log_error(handler_name))?;

    let pool = get_conn(&state);

    let url = match service::url::grab_add(
        &pool,
        model::url::Url {
            user_id: cliams.data.id.clone(),
            origin: frm.origin.clone(),
            has_password: frm.has_password(),
            password: frm.hashed_password().unwrap_or_default(),
            has_expired: frm.has_expired(),
            expired: frm.parsed_expired().unwrap_or_default(),
            dateline: Local::now(),
            ..Default::default()
        },
        None,
    )
    .await
    .map_err(log_error(handler_name))?
    {
        Some(v) => v,
        None => return Err(Error::internal_server("无法创建短链接")),
    };

    Ok(Response::ok(IDResponse { id: url.id }).to_json())
}

pub async fn list(
    State(state): State<Arc<AppState>>,
    Query(frm): Query<form::url::List>,
) -> Result<Json<Response<db::Pagination<model::user_url::UserUrl>>>> {
    let handler_name = "admin/url/list";

    frm.validate()
        .map_err(Error::from)
        .map_err(log_error(handler_name))?;

    let pool = get_conn(&state);

    let ls = service::user_url::list(
        &pool,
        &filter::user_url::List {
            pagination: filter::Pagination {
                page: frm.p.page(),

                ..Default::default()
            },
            url: None,
            email: None,
            origin: None,
            has_password: None,
            has_expired: None,
            expired_range: None,
            order: None,
        },
    )
    .await
    .map_err(log_error(handler_name))?;

    Ok(Response::ok(ls).to_json())
}

pub async fn edit(
    State(state): State<Arc<AppState>>,
    // RequiredAuth(cliams): RequiredAuth<AdminClaimsData>,
    Path(id): Path<String>,
    Json(frm): Json<form::url::Edit>,
) -> Result<Json<Response<AffResponse>>> {
    let handler_name = "admin/url/edit";
    frm.validate()
        .map_err(Error::from)
        .map_err(log_error(handler_name))?;

    let pool = get_conn(&state);

    let m = match service::url::find(&pool, &filter::url::FindBy::ID(&id))
        .await
        .map_err(log_error(handler_name))?
    {
        Some(v) => v,
        None => return Err(Error::not_found("不存在的链接")),
    };

    let m = model::url::Url {
        password: frm.hashed_password().unwrap_or_default(),
        expired: frm.parsed_expired().unwrap_or_default(),
        has_expired: frm.has_expired(),
        has_password: frm.has_password(),
        ..m
    };

    let rows = service::url::edit(&pool, &m, frm.skip_update_password())
        .await
        .map_err(log_error(handler_name))?;

    Ok(Response::ok(AffResponse { rows }).to_json())
}

pub async fn del(
    State(state): State<Arc<AppState>>,

    Path(id): Path<String>,
) -> Result<Json<Response<AffResponse>>> {
    let handler_name = "admin/url/del";
    let pool = get_conn(&state);

    let rows = service::url::del(&pool, &id, None)
        .await
        .map_err(log_error(handler_name))?;
    Ok(Response::ok(AffResponse { rows }).to_json())
}
